package org.stragma.apply.modules

import org.kiama.rewriting._
import org.kiama.rewriting.Rewriter.{rule,rulefs,Strategy,innermost,Term}

import org.stragma.apply.signatures.Apply._
import org.stragma.apply.util._

/**
 * ApplyParen is a port of the ApplyParen module from Apply
 * it's primary task is to apply parenthesis as specified in the rules
 * contained below
 */
object ApplyParen extends TermInputOutput {

	/*
	 * Parenthesize Apply Helper For Java Access
	 */
	def parenthesizeApplyHelper(term : Term) : Term = {
		(parenthesizeApply(term)).get
	}
	
	/*
	 * This helper method executes parenthesizeApplyHelper n times and returns
	 * the very last result the caller
	 */
	def parenthesizeApplyHelper(term : Term, n : scala.Int) : Term = {
		var result : Term = scala.None
		for(i <- 0 until n) {
			result = (parenthesizeApply(term)).get
			println(result)
		}
		
		result
	}
	
	/*
	 * This helper method executes applyDesugar n times and returns
	 * the very last result to the caller
	 */
	def parenthesizeApplyHelper(file : String, n : scala.Int) : Term = {
		val iTerm = loadTermFromFile(file)
		parenthesizeApplyHelper(iTerm, n)
	}
	
	/*
	 * Port of the parenthesizeApply strategy which uses the innermost traversal strategy
	 */
	def parenthesizeApply = innermost(applyParenthesize)
	
	/*
	 * These are all the rules from Apply_paren.str
	 */
	def applyParenthesize : Strategy = {
		rulefs {
			case Neg(t_0) 
				if( applyNegGuard(t_0) == Some(t_0) ) => 
					Neg( Parenthetical(t_0) )
					
			case Mul(t_0, t_1) 
				if(applyDivMulLeftGuard(t_0) == Some(t_0)) => 
					Mul( Parenthetical(t_0), t_1 )

			case Mul(t_0, t_1) 
				if( applyDivMulRightGuard(t_1) == Some(t_1) ) => 
					Mul( t_0, Parenthetical(t_1) )
					
			case Div(t_0, t_1) 
				if( applyDivMulLeftGuard(t_0) == Some(t_0) ) => 
					Div( Parenthetical(t_0), t_1 )
					
			case Div(t_0, t_1) 
				if( applyDivMulRightGuard(t_1) == Some(t_1) ) => 
					Div( t_0, Parenthetical(t_1) )
					
			case Mod(t_0, t_1) 
				if( applyModLeftGuard(t_0) ==  Some(t_0) ) => 
					Mod( Parenthetical(t_0), t_1 )
					
			case Mod(t_0, t_1) 
				if( applyModRightGuard(t_1) ==  Some(t_1) ) => 
					Mod( t_0, Parenthetical(t_1) )
					
			case Add(t_0, t_1) 
				if( applyAddSubLeftGuard(t_0) ==  Some(t_0) ) => 
					Add( Parenthetical(t_0), t_1 )
					
			case Add(t_0, t_1) 
				if( applyAddSubRightGuard(t_1) ==  Some(t_1) ) => 
					Add( t_0, Parenthetical(t_1) )
					
			case Sub(t_0, t_1) 
				if( applyAddSubLeftGuard(t_0) ==  Some(t_0) ) => 
					Sub( Parenthetical(t_0), t_1 )
					
			case Sub(t_0, t_1) 
				if( applyAddSubRightGuard(t_1) ==  Some(t_1) ) => 
					Sub( t_0, Parenthetical(t_1) )
					
			case BitAnd(t_0, t_1) 
				if( applyBitAndLeftGuard(t_0) ==  Some(t_0) ) => 
					BitAnd( Parenthetical(t_0), t_1 )
					
			case BitAnd(t_0, t_1) 
				if( applyBitAndRightGuard(t_1) ==  Some(t_1) ) => 
					BitAnd( t_0, Parenthetical(t_1) )
					
			case BitOr(t_0, t_1) 
				if( applyBitOrLeftGuard(t_0) ==  Some(t_0) ) => 
					BitOr( Parenthetical(t_0), t_1 )
					
			case BitOr(t_0, t_1) 
				if( applyBitOrRightGuard(t_1) ==  Some(t_1) ) => 
					BitOr( t_0, Parenthetical(t_1) )
					
			case Lt(t_0, t_1) 
				if( applyGtAndLtGuard(t_0) ==  Some(t_0) ) => 
					Lt( Parenthetical(t_0), t_1 )
					
			case Lt(t_0, t_1) 
				if( applyGtAndLtGuard(t_1) ==  Some(t_1) ) => 
					Lt( t_0, Parenthetical(t_1) )
					
			case Gt(t_0, t_1) 
				if( applyGtAndLtGuard(t_0) ==  Some(t_0) ) => 
					Gt( Parenthetical(t_0), t_1 )
				
			case Gt(t_0, t_1) 
				if( applyGtAndLtGuard(t_1) ==  Some(t_1) ) => 
					Gt( t_0, Parenthetical(t_1) )
					
			case Leq(t_0, t_1) 
				if( applyGeqAndLeqGuard(t_0) ==  Some(t_0) ) => 
					Leq( Parenthetical(t_0), t_1 )
					
			case Leq(t_0, t_1) 
				if( applyGeqAndLeqGuard(t_1) ==  Some(t_1) ) => 
					Leq( t_0, Parenthetical(t_1) )
					
			case Geq(t_0, t_1) 
				if( applyGeqAndLeqGuard(t_0) ==  Some(t_0) ) => 
					Geq( Parenthetical(t_0), t_1 )
				
			case Geq(t_0, t_1) 
				if( applyGeqAndLeqGuard(t_1) ==  Some(t_1) ) => 
					Geq( t_0, Parenthetical(t_1) )
					
			case Equ(t_0, t_1) 
				if( applyNeqAndEqGuard(t_0) ==  Some(t_0) ) => 
					Equ( Parenthetical(t_0), t_1 )
					
			case Equ(t_0, t_1) 
				if( applyNeqAndEqGuard(t_1) == Some(t_1) ) => 
					Equ( t_0, Parenthetical(t_1) )
					
			case Neq(t_0, t_1) 
				if( applyNeqAndEqGuard(t_0) ==  Some(t_0) ) => 
					Neq( Parenthetical(t_0), t_1 )
					
			case Neq(t_0, t_1) 
				if( applyNeqAndEqGuard(t_1) == Some(t_1) ) => 
					Neq( t_0, Parenthetical(t_1) )
					
			case Not(t_0) 
				if( applyNotGuard(t_0) == Some(t_0) ) => 
					Not( Parenthetical(t_0) )
					
			case And(t_0, t_1) 
				if( applyAndLeftGuard(t_0) == Some(t_0) ) => 
					And( Parenthetical(t_0), t_1 )
					
			case And(t_0, t_1) 
				if( applyAndRightGuard(t_1) == Some(t_1) ) => 
					And( t_0, Parenthetical(t_1) )
					
			case Or(t_0, t_1) 
				if( applyOrLeftGuard(t_0) == Some(t_0) ) => 
					Or( Parenthetical(t_0), t_1 )
					
			case Or(t_0, t_1) 
				if( applyOrRightGuard(t_1) == Some(t_1) ) => 
					Or( t_0, Parenthetical(t_1) )
				
			case CAssign(t_0, t_1) 
				if( applyCAssignParenthesizeGuard(t_0) == Some(t_0) ) => 
					CAssign( Parenthetical1(t_0), t_1 )
				
			case CAssign(t_0, t_1) 
				if( applyCAssignParenthesizeGuard(t_1) == Some(t_1) ) => 
					CAssign( t_0, Parenthetical(t_1) )
				
			case Comma(t_0, t_1) 
				if( applyCommaParenthesizeGuard(t_1) == Some(t_1) ) => 
					Comma( t_0, Parenthetical(t_1) )
		}
	}
	
	/*
	 * Used in the guard condition for the case Neg(t_0)
	 */
	private val applyNegGuard : Strategy = {
		rule { case e : Mul	=> e } <+
		rule { case e : Div	=> e } <+
		rule { case e : Mod => e } <+
		rule { case e : Add => e } <+
		rule { case e : Sub => e } <+
		rule { case e : BitAnd => e } <+
		rule { case e : BitOr	=> e } <+
		rule { case e : Lt	=> e } <+
		rule { case e : Gt	=> e } <+
		rule { case e : Leq	=> e } <+
		rule { case e : Geq => e } <+
		rule { case e : Equ	=> e } <+
		rule { case e : Neq => e } <+
		rule { case e : Not => e } <+
		rule { case e : And => e } <+
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for the case Div(t_0,t_1) & Mul(t_0,t_1)
	 */
	private val applyDivMulLeftGuard : Strategy = {
		rule { case e : Add	=> e } <+
		rule { case e : Sub	=> e } <+
		rule { case e : BitAnd => e } <+
		rule { case e : BitOr => e } <+
		rule { case e : Lt => e } <+
		rule { case e : Gt => e } <+
		rule { case e : Leq	=> e } <+
		rule { case e : Geq	=> e } <+
		rule { case e : Equ	=> e } <+
		rule { case e : Neq	=> e } <+
		rule { case e : Not => e } <+
		rule { case e : And	=> e } <+
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for the case Div(t_0,t_1) & Mul
	 */
	private val applyDivMulRightGuard : Strategy = {
		rule { case e : Add	=> e } <+
		rule { case e : Sub	=> e } <+
		rule { case e : BitAnd => e } <+
		rule { case e : BitOr => e } <+
		rule { case e : Lt => e } <+
		rule { case e : Gt => e } <+
		rule { case e : Leq	=> e } <+
		rule { case e : Geq	=> e } <+
		rule { case e : Equ	=> e } <+
		rule { case e : Neq	=> e } <+
		rule { case e : Not => e } <+
		rule { case e : And	=> e } <+
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		rule { case e : Mul	=> e } <+
		rule { case e : Mod	=> e } <+
		rule { case e : Div => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for the case Mod(t_0,t_1)
	 */
	private val applyModLeftGuard : Strategy = {
		rule { case e : Add => e } <+
		rule { case e : Sub => e } <+
		rule { case e : BitAnd => e } <+
		rule { case e : BitOr => e } <+
		rule { case e : Lt => e } <+
		rule { case e : Gt => e } <+
		rule { case e : Leq => e } <+
		rule { case e : Geq => e } <+
		rule { case e : Equ => e } <+
		rule { case e : Neq => e } <+
		rule { case e : Not => e } <+
		rule { case e : And => e } <+
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		rule { case e : Mod => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for the case Mod(t_0,t_1)
	 */
	private val applyModRightGuard : Strategy = {
		rule { case e : Add => e } <+
		rule { case e : Sub => e } <+
		rule { case e : BitAnd => e } <+
		rule { case e : BitOr => e } <+
		rule { case e : Lt => e } <+
		rule { case e : Gt => e } <+
		rule { case e : Leq => e } <+
		rule { case e : Geq => e } <+
		rule { case e : Equ => e } <+
		rule { case e : Neq => e } <+
		rule { case e : Not => e } <+
		rule { case e : And => e } <+
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		rule { case e : Mul => e } <+
		rule { case e : Div => e } <+
		rule { case e : Mod => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for the case Sub(t_0,t_1) & Add(t_0,t_1)
	 */
	private val applyAddSubLeftGuard : Strategy = {
		rule { case e : BitAnd => e } <+
		rule { case e : BitOr => e } <+
		rule { case e : Lt => e } <+
		rule { case e : Gt => e } <+
		rule { case e : Leq => e } <+
		rule { case e : Geq => e } <+
		rule { case e : Equ => e } <+
		rule { case e : Neq => e } <+
		rule { case e : Not => e } <+
		rule { case e : And => e } <+
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for the case Sub(t_0,t_1) & Add(t_0,t_1)
	 */
	private val applyAddSubRightGuard : Strategy = {
		rule { case e : BitAnd => e } <+
		rule { case e : BitOr => e } <+
		rule { case e : Lt => e } <+
		rule { case e : Gt => e } <+
		rule { case e : Leq => e } <+
		rule { case e : Geq => e } <+
		rule { case e : Equ => e } <+
		rule { case e : Neq => e } <+
		rule { case e : Not => e } <+
		rule { case e : And => e } <+
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		rule { case e : Add => e } <+
		rule { case e : Sub => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for the case BitAnd(t_0,t_1)
	 */
	private val applyBitAndLeftGuard : Strategy = {
		rule { case e : BitOr => e } <+
		rule { case e : Lt => e } <+
		rule { case e : Gt => e } <+
		rule { case e : Leq => e } <+
		rule { case e : Geq => e } <+
		rule { case e : Equ => e } <+
		rule { case e : Neq => e } <+
		rule { case e : Not => e } <+
		rule { case e : And => e } <+
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for the case BitAnd(t_0,t_1)
	 */
	private val applyBitAndRightGuard : Strategy = {
		rule { case e : BitOr => e } <+
		rule { case e : Lt => e } <+
		rule { case e : Gt => e } <+
		rule { case e : Leq => e } <+
		rule { case e : Geq => e } <+
		rule { case e : Equ => e } <+
		rule { case e : Neq => e } <+
		rule { case e : Not => e } <+
		rule { case e : And => e } <+
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		rule { case e : BitAnd => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for the case BitOr(t_0,t_1)
	 */
	private val applyBitOrLeftGuard : Strategy = {
		rule { case e : Lt => e } <+
		rule { case e : Gt => e } <+
		rule { case e : Leq => e } <+
		rule { case e : Geq => e } <+
		rule { case e : Equ => e } <+
		rule { case e : Neq => e } <+
		rule { case e : Not => e } <+
		rule { case e : And => e } <+
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for the case BitOr(t_0,t_1)
	 */
	private val applyBitOrRightGuard : Strategy = {
		rule { case e : Lt => e } <+
		rule { case e : Gt => e } <+
		rule { case e : Leq => e } <+
		rule { case e : Geq => e } <+
		rule { case e : Equ => e } <+
		rule { case e : Neq => e } <+
		rule { case e : Not => e } <+
		rule { case e : And => e } <+
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		rule { case e : BitOr => e } <+
		applyCatchAllFail
	}
		
	/*
	 * Used in the guard condition for the case Gt(t_0,t_1) and Lt(t_0,t_1)
	 */
	private val applyGtAndLtGuard : Strategy = {
		rule { case e : Not => e } <+
		rule { case e : And => e } <+
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		rule { case e : Lt => e } <+
		rule { case e : Leq => e } <+
		rule { case e : Geq => e } <+
		rule { case e : Equ => e } <+
		rule { case e : Neq => e } <+
		rule { case e : Gt => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for the case Geq(t_0,t_1) and Leq(t_0,t_1)
	 */
	private val applyGeqAndLeqGuard : Strategy = {
		rule { case e : Not => e } <+
		rule { case e : And => e } <+
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		rule { case e : Lt => e } <+
		rule { case e : Gt => e } <+
		rule { case e : Leq => e } <+
		rule { case e : Equ => e } <+
		rule { case e : Neq => e } <+
		rule { case e : Geq => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for the case Neq(t_0,t_1) and Equ(t_0,t_1)
	 */
	private val applyNeqAndEqGuard : Strategy = {
		rule { case e : Not => e } <+
		rule { case e : And => e } <+
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		rule { case e : Lt => e } <+
		rule { case e : Gt => e } <+
		rule { case e : Leq => e } <+
		rule { case e : Geq => e } <+
		rule { case e : Equ => e } <+
		rule { case e : Neq => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for the case Not(t_0)
	 */
	private val applyNotGuard : Strategy = {
		rule { case e : And => e } <+
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for case And(t_0,t_1)
	 */
	private val applyAndLeftGuard : Strategy = {
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for case And(t_0,t_1)
	 */
	private val applyAndRightGuard : Strategy = {
		rule { case e : Or => e } <+
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		rule { case e : And => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for case Or(t_0,t_1)
	 */
	private val applyOrLeftGuard : Strategy = {
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition for case Or(t_0,t_1)
	 */
	private val applyOrRightGuard : Strategy = {
		rule { case e : CAssign => e } <+
		rule { case e : Comma => e } <+
		rule { case e : Or => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition of case case CAssign(t_0 : Exp, t_1 : Exp)
	 */
	private val applyCAssignParenthesizeGuard : Strategy = {
		rule { case e: CAssign => e } <+
		applyCatchAllFail
	}
	
	/*
	 * Used in the guard condition of case Comma(t_0 : Exp, t_1 : Exp)
	 */
	private val applyCommaParenthesizeGuard : Strategy = {
		rule { case e : Comma => e } <+
		applyCatchAllFail
	}
	
	/*
	 * The fail strategy is encapsulated behind this val for re-use
	 */
	private val applyCatchAllFail : Strategy = rule { case _ => "fail" }
}